﻿Imports Microsoft.VisualBasic
Imports System.Collections.Specialized
Imports System.Web
Imports System.Text.RegularExpressions
Imports System.Text
''' <summary> 
''' Class To parse QueryString
''' </summary> 
Public Class HttpUrlQuery
    Private start As String = "?"
    Private separator_pairs As String = "&"
    Private separator_values As String = "="

    Private original As String = ""

    Private qs As NameValueCollection

    ''' <summary> 
    ''' Default constructor 
    ''' </summary> 
    Public Sub New()
        qs = New NameValueCollection(0)
    End Sub

    ''' <summary> 
    ''' Constructor from a string that already contains a querystring. No url encoding is done. Standard separation characters are used. 
    ''' </summary> 
    ''' <param name="QueryString">Querystring string source</param> 
    Public Sub New(ByVal QueryString As String)
        Me.New(QueryString, False)
    End Sub


    ''' <summary> 
    ''' Constructor from a string that already contains a querystring. Standard separation characters are used. 
    ''' </summary> 
    ''' <param name="QueryString">Querystring string source</param> 
    ''' <param name="UrlEncode">Whether or not standard Http url encoding should be performed on the values</param> 
    Public Sub New(ByVal QueryString As String, ByVal UrlEncode As Boolean)
        Me.New(QueryString, UrlEncode, "?", "&", "=")
    End Sub

    ''' <summary> 
    ''' Constructor from a string that already contains a querystring. Custom separation characters can be used. 
    ''' </summary> 
    ''' <param name="QueryString">Querystring string source</param> 
    ''' <param name="UrlEncode">Whether or not standard Http url encoding should be performed on the values</param> 
    ''' <param name="StartString">String that appears in the front of the query string. Used in the ToString() output.</param> 
    ''' <param name="SeparatorPairsString">String that appears between pairs of the query string. Used in the ToString() output.</param> 
    ''' <param name="SeparatorNameValueString">String that appears between name and value in each pair of the query string. Used in the ToString() output.</param> 
    Public Sub New(ByVal QueryString As String, ByVal UrlEncode As Boolean, ByVal StartString As String, ByVal SeparatorPairsString As String, ByVal SeparatorNameValueString As String)
        original = QueryString

        start = StartString
        separator_pairs = SeparatorPairsString
        separator_values = SeparatorNameValueString

        QueryString = QueryString.Replace(start, "")

        Dim parts As String() = Regex.Split(QueryString, separator_pairs)

        qs = New NameValueCollection(parts.Length)

        Dim subparts As String()

        For Each part As String In parts
            subparts = Regex.Split(part, separator_values)

            If subparts.Length = 0 Then
                [Set](subparts(0), "")
            ElseIf subparts.Length > 0 Then
                [Set](subparts(0), subparts(1), UrlEncode)

            End If

        Next
    End Sub

    ''' <summary> 
    ''' Constructor from another HttpUrlQuery object. Deep copy is performed to make sure the new object becomes completely independent of its source. 
    ''' </summary> 
    ''' <param name="Source">Another object whose state will be used to create this new object.</param> 
    Public Sub New(ByVal Source As HttpUrlQuery)
        original = Source.ToString()

        start = Source.StartString
        separator_pairs = Source.SeparatorPairsString
        separator_values = Source.SeparatorNameValueString

        Dim keys As String() = Source.AllKeys

        qs = New NameValueCollection(keys.Length)

        For Each key As String In keys
            [Set](key, Source.[Get](key))

        Next
    End Sub

    ''' <summary> 
    ''' Constructor from a NameValueCollection, such as one usually found in System.Web.UI.Page.Request.Querystring objects. No url encoding is done. 
    ''' </summary> 
    ''' <param name="QueryString">Querystring collection source</param> 
    Public Sub New(ByVal QueryString As NameValueCollection)
        Me.New(QueryString, False)
    End Sub


    ''' <summary> 
    ''' Constructor from a NameValueCollection, such as one usually found in System.Web.UI.Page.Request.Querystring objects. 
    ''' </summary> 
    ''' <param name="QueryString">Querystring collection source</param> 
    ''' <param name="UrlEncode">Whether or not standard Http url encoding should be performed on the values</param> 
    Public Sub New(ByVal QueryString As NameValueCollection, ByVal UrlEncode As Boolean)
        qs = New NameValueCollection(QueryString.Count)

        ' copy each pair 
        For Each q As String In QueryString
            [Set](q, QueryString.[Get](q), UrlEncode)
        Next


        original = ToString()
    End Sub

    ''' <summary> 
    ''' Removes pair with given name. 
    ''' </summary> 
    ''' <param name="Name">Name of the pair to be removed.</param> 
    Public Sub Remove(ByVal Name As String)
        qs.Remove(Name)
    End Sub

    ''' <summary> 
    ''' Determines whether pair with given name exists in this query string. Case insensitive. 
    ''' </summary> 
    ''' <param name="Name">Name of the desired pair.</param> 
    ''' <returns>Whether or not given pair exists in this query string.</returns> 
    Public Function Contains(ByVal Name As String) As Boolean
        If Not Name Is Nothing Then
            Name = Name.ToLower()

            For Each x As String In qs.Keys
                If x.ToLower() = Name Then
                    Return True
                End If
            Next

        End If

        Return False
    End Function

    ''' <summary> 
    ''' Sets the value of the pair with given name. Adds new pair, if doesn't already exist. Url encoding is performed on NewValue. 
    ''' </summary> 
    ''' <param name="Name">Name of the desired pair.</param> 
    ''' <param name="NewValue">New value to be assigned to this pair. Will be forced to undergo url encoding.</param> 
    Public Sub [Set](ByVal Name As String, ByVal NewValue As String)
        [Set](Name, NewValue, True)
    End Sub


    ''' <summary> 
    ''' Retreives the value of the pair with given name. 
    ''' </summary> 
    ''' <param name="Name">Name of the desired pair.</param> 
    Public Function [Get](ByVal Name As String) As String
        Return (IIf(Contains(Name), qs.[Get](Name), ""))

    End Function

    ''' <summary> 
    ''' Returns all keys in this query string 
    ''' </summary> 
    Public ReadOnly Property AllKeys() As String()
        Get
            Return qs.AllKeys
        End Get
    End Property


    ''' <summary> 
    ''' Allows for square-bracket referencing of elements 
    ''' </summary> 
    Default Public Property Item(ByVal Name As String) As String
        Get
            Return [Get](Name)
        End Get

        Set(ByVal value As String)
            [Set](Name, value)
        End Set
    End Property


    ''' <summary> 
    ''' Sets the value of the pair with given name. Adds new pair, if doesn't already exist. No url encoding is done. 
    ''' </summary> 
    ''' <param name="Name">Name of the desired pair.</param> 
    ''' <param name="NewValue">New value to be assigned to this pair.</param> 
    ''' <param name="UrlEncode">Whether or not standard Http url encoding should be performed on the values</param> 
    Public Sub [Set](ByVal Name As String, ByVal NewValue As String, ByVal UrlEncode As Boolean)
        If UrlEncode Then
            NewValue = System.Web.HttpContext.Current.Server.UrlEncode(NewValue)
        End If

        If Contains(Name) Then
            qs.[Set](Name, NewValue)
        Else
            qs.Add(Name, NewValue)
        End If
    End Sub

    ''' <summary> 
    ''' Defines the string that preceeds the actual pairs in the string output of this query string. "?" by default. 
    ''' </summary> 
    Public Property StartString() As String
        Get
            Return start
        End Get

        Set(ByVal value As String)
            start = value
        End Set
    End Property

    ''' <summary> 
    ''' Defines the string that separates pairs. "&amp;" by default. 
    ''' </summary> 
    Public Property SeparatorPairsString() As String
        Get
            Return separator_pairs
        End Get

        Set(ByVal value As String)
            separator_pairs = value
        End Set
    End Property

    ''' <summary> 
    ''' Defines the string that separates name from value within a pair. "=" by default. 
    ''' </summary> 
    Public Property SeparatorNameValueString() As String
        Get
            Return separator_values
        End Get

        Set(ByVal value As String)
            separator_values = value
        End Set
    End Property

    ''' <summary> 
    ''' An instance of the querystring object that is based on the querystring in the current context 
    ''' </summary> 
    Public Shared ReadOnly Property Current() As HttpUrlQuery
        Get
            Return New HttpUrlQuery(HttpContext.Current.Request.QueryString)
        End Get
    End Property

    ''' <summary> 
    ''' Represenation of the original string which was used to initialize this object 
    ''' </summary> 
    Public ReadOnly Property OriginalString() As String
        Get
            Return original
        End Get
    End Property


    ''' <summary> 
    ''' String representation of contained querystring. 
    ''' </summary> 
    ''' <returns></returns> 
    Public Overloads Overrides Function ToString() As String
        If qs.Count < 1 Then
            Return ""
        End If

        Dim b As New StringBuilder()

        b.Append(start)

        Dim s As String = ""
        For i As Integer = 0 To qs.Count - 1

            s = qs.Keys(i)

            b.Append(s)
            b.Append(separator_values)
            b.Append(qs.[Get](s))

            If i < (qs.Count - 1) Then
                b.Append(separator_pairs)

            End If
        Next

        Return b.ToString()
    End Function
End Class
